
import serial
import time
from threading import Thread
from traceback import print_exc 

PERIPHERALS = ['actuators', 'ultrasound', 'encoders', 'arm', ]

### peripheral : (input, output,)
CONNECTIONS = {
	'actuators'		: (None, 'COM3', ),
	'arm'			: ('COM10', 'COM10', ),
	'ultrasound'	: ('COM11', None, ),
	'encoders'		: ('COM4', 'COM4', ),
	}
BAUDRATES = {
	'COM10' : 2400,
	}
DEFAULT_BAUDRATE = 9600

class CommPipe:
	""" objec used by external logic
	"""
	def __init__(self, io, output, input=None):
		self._io = io
		self._output = output
		self._input = input
		self._notify = False
	
	def write(self, data):
		self._io.write_to_port(self._output, data)
	
	def isr(self, data, time_stamp):
		""" owerride this in any way conviniant,
			will be in IO threadspace
		"""
		if not self._notify:
			print 'ISR METHOD NOT OVERRIDEN !, DATA LOST ON PORT %s' % (self._input, )
			self._notify = True

class IO:
	""" Handles communication with peripherals
	"""
	def __init__(self):
		#Thread.__init__(self)
		self._portnames = []
		self._ports = {}
		##
		self._inputs = {}
		self._threads = []
		self._not_listed_msgs = []
	
	def open_port(self, name):
		if name in self._portnames: return
		print '[IO] open port %s' % (name, )
		if BAUDRATES.has_key(name):
			baud_rate = BAUDRATES[name]
		else: baud_rate = DEFAULT_BAUDRATE
		try:
			port = serial.Serial(port=name, baudrate=baud_rate)
			self._portnames.append(name)
			self._ports[name] = port
		except serial.SerialException, ex:
			print ' -- open error on %s port -- ' % (name, )
			print ' -- %s -- ' % (ex, )
			#print_exc()
	
	def init(self):
		""" init from globals
		"""
		for peripheral, (input, output,), in CONNECTIONS.items():
			if input: 
				self.open_port(input)
				if not self._inputs.has_key(input):
					self._inputs[input] = []
			if output: 
				self.open_port(output)
		for portname in self._portnames:
			t = Thread(target=self.isr, args=(portname,))
			t.setDaemon(True)
			t.start()
			self._threads.append(t)
	
	def isr(self, name):
		port = self._ports[name]
		while 1:
			new_chunk = port.read()
			if new_chunk:
				#print ' --> ', repr(new_chunk)
				time_stamp = time.time()
				for handler in self._inputs[name]:
					#print 'got handler'
					handler.isr(new_chunk, time_stamp)
			else :
				time.sleep(0.0001)
	
	def write_to_port(self, portname, data):
		if self._ports.has_key(portname):
			port = self._ports[portname]
			port.write(data)
		else:
			if portname not in self._not_listed_msgs:
				print '[IO] %s is not listed' % (portname, )
				self._not_listed_msgs.append( portname )

	def link(self, peripheral_name, isr_method=None):	
		""" connect communication link
		"""
		if CONNECTIONS.has_key(peripheral_name):
			input, output, = CONNECTIONS[peripheral_name]
			pipe = CommPipe(self, output, input)
			if input:
				if not self._inputs.has_key(input):
					print 'FORCED ADD for %s' % (input, )
					self._inputs[input] = []
				handlers = self._inputs[input]
				handlers.append(pipe)
				if isr_method: pipe.isr = isr_method
			return pipe
		else:
			print '[IO] edit CONNECTIONS and add %s peripheral to it !' % (peripheral_name, )


Comm = IO()
Comm.init()
